package treeedit;

import java.text.ParseException;
import java.io.Reader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;

public class CodeParser {

	private final char PARENTH_OPEN = '(';
	private final char PARENTH_CLOSE = ')';
	private final char QUOTE = '`';
	private final char QUOTE2 = '\'';
	private final char STRING = '\"';
	private final char ESCAPE = '\\';
	private final char COMMENT = ';';
	private final char SPACE = ' ';
	private final char HASH = '#';
	private Collection<Character> separators;

	private String string;
	private int i;

	public CodeParser() {
		char[] sepArray = {PARENTH_OPEN, PARENTH_CLOSE, SPACE};
		separators = new LinkedList<Character>();
		for(char c : sepArray)
			separators.add(c);
	}

	public CodeNode parse (Reader reader) throws ParseException {
		BufferedReader in = new BufferedReader(reader);
		String line;
		StringBuffer all = new StringBuffer("");
		try {
			while ((line = in.readLine())!=null)
				all.append("\n" + line);
		} catch (IOException exc) {
			System.err.println("CodeParser: Can't use reader");
		}
		return parse(all.toString());
	}

	public CodeNode parse (String string) throws ParseException {
		CodeNode root = new CodeNode("//root//");
		this.string = string.trim();
		this.i = 0;
		while (i < this.string.length()) {
			root.add(parseElement(root));
			try {
				skipWhiteSpaceAndComment();
			} catch (ParseException ex) {/*Not so unexpected*/}
		}
		return root;
	}

	//-------------------------------------------------------------
	private CodeNode parseElement(CodeNode parent) throws ParseException {
		skipWhiteSpaceAndComment();
		switch (string.charAt(i)) {
			case PARENTH_CLOSE: throw new ParseException("Too many parenthesis closing",i);
			case PARENTH_OPEN: return parseList(parent);
			case STRING: return parseString(parent);
			case QUOTE: case QUOTE2: return parseQuote(parent);
			case HASH: return parseFunction(parent);
			default: return parseAtom(parent); 
		}
	}

	private void skipWhiteSpaceAndComment() throws ParseException {
		while(i<string.length() && Character.isWhitespace(string.charAt(i))) i++;
		if(i>=string.length()) throw new ParseException("Unexpected end of file", i);
		if(string.charAt(i)==COMMENT) {
			while(i<string.length() && string.charAt(i)!='\n') {
				i++;
			}
			if(i>=string.length()) throw new ParseException("Unexpected end of file", i);
			i++; // to next line
			skipWhiteSpaceAndComment();
		}
	}

	private CodeNode parseAtom(CodeNode parent) throws ParseException {
		int begin = i;
		while(i<string.length() && !separators.contains(string.charAt(i))) i++;
		return new CodeNode(string.substring(begin,i), parent);
	}

	private CodeNode parseList(CodeNode parent) throws ParseException {
		i++; // step out of '('
		skipWhiteSpaceAndComment();
		CodeNode node = new CodeNode("( )",parent);
		while(string.charAt(i)!=PARENTH_CLOSE) {
			node.add(parseElement(node));
			skipWhiteSpaceAndComment();
		}
		i++; // step out of ')'
		return node;
	}

	private CodeNode parseString(CodeNode parent) throws ParseException {
		int begin = i;
		i++;// step out of "
		while(i < string.length() && (string.charAt(i)!=STRING || string.charAt(i-1)==ESCAPE)) {
			i++;
		}
		i++;// step out of "
		CodeNode ret =  new CodeNode(string.substring(begin+1,i-1),parent);
		ret.setString(true);
		return ret;
	}

	private CodeNode parseQuote(CodeNode parent) throws ParseException {
		char quoteUsed = string.charAt(i);
		i++; // step out of `
		skipWhiteSpaceAndComment();
		CodeNode node = parseElement(parent);
		//System.err.println(node.getShortcut());
		node.setCode(quoteUsed + node.getCode());
		node.setQuote(true);
		return node;
	}

	private CodeNode parseFunction(CodeNode parent) throws ParseException {
		i++; // step out of #
		skipWhiteSpaceAndComment();
		CodeNode node = parseElement(parent);
		//System.err.println(node.getShortcut());
		node.setCode('#' + node.getCode());
		return node;
	}
	//--------------------------------------------------------------
}
